home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / bit / src / forms / FORMS / chart.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  13KB  |  438 lines

  1. /*
  2.  * chart.c
  3.  *
  4.  * Forms Object class: CHART
  5.  *
  6.  * Written by: Mark Overmars
  7.  *
  8.  * Version 2.2 a
  9.  * Date: Jun 21, 1993
  10.  */
  11.  
  12. #include <malloc.h>
  13. #include "gl/gl.h"
  14. #include "gl/device.h"
  15. #include <sys/types.h>
  16. #include <math.h>
  17. #include <string.h>
  18. #include "forms.h"
  19.  
  20. #define PI    3.14159265
  21. #define ARCINC    (2.0*PI/3600.0)
  22.  
  23. /* Object specific information */
  24.  
  25. typedef struct{
  26.    float val;        /* Value of the entry */
  27.    char str[16];    /* Label of the entry */
  28.    int col;        /* Color of the entry */
  29. } ENTRY;
  30.  
  31. typedef struct{
  32.    int numb;            /* Number of entries */
  33.    int maxnumb;            /* Maximal number of entries to display */
  34.    ENTRY entries[FL_CHART_MAX+1];/* The entries */
  35.    float min,max;        /* The boundaries */
  36.    int autosize;        /* Whether the x-axis should be scaled */
  37. } SPEC;
  38.  
  39. static void vv(float x, float y)
  40.   { short v[2]; v[0] = (short) x; v[1] = (short) y; v2s(v);}
  41.  
  42. static void draw_barchart(float x, float y, float w, float h,
  43.             int numb, ENTRY entries[],
  44.             float min, float max, int autosize, int maxnumb)
  45. /* Draws a bar chart. x,y,w,h is the bounding box, entries the array of
  46.    numb entries and min and max the boundaries. */
  47. {
  48.   int i;
  49.   float bwidth;            /* Width of a bar */
  50.   float zeroh;            /* Height of zero value */
  51.   float incr;            /* Increment per unit value */
  52.   float lh = fl_get_char_height(FL_SMALL_FONT,FL_NORMAL_STYLE);
  53.   incr = h/ (max-min);
  54.   zeroh = y-min * incr;
  55.   if ( -min*incr < lh)
  56.     { incr = (h - lh + min*incr)/(max-min); zeroh = y+lh;}
  57.   if (autosize) bwidth = w/numb; else bwidth = w/maxnumb;
  58.   /* Draw base line */
  59.   fl_color(BLACK);
  60.   bgnline(); vv(x,zeroh); vv(x+w,zeroh); endline();
  61.   if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */
  62.   /* Draw the bars */
  63.   for (i=0; i<numb; i++)
  64.     fl_rectbound(x+i*bwidth,zeroh,bwidth+1.0, entries[i].val*incr+1.0,
  65.             entries[i].col);
  66.   /* Draw the labels */
  67.   fl_color(BLACK);
  68.   for (i=0; i<numb; i++)
  69.     fl_drw_text_beside(FL_ALIGN_BOTTOM,x+(i+0.5)*bwidth,zeroh,0.0,0.0,
  70.             BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
  71. }
  72.  
  73. static void draw_horbarchart(float x, float y, float w, float h,
  74.             int numb, ENTRY entries[],
  75.             float min, float max, int autosize, int maxnumb)
  76. /* Draws a horizontal bar chart. x,y,w,h is the bounding box, entries the
  77.    array of numb entries and min and max the boundaries. */
  78. {
  79.   int i;
  80.   float bwidth;            /* Width of a bar */
  81.   float zeroh;            /* Position of zero value */
  82.   float incr;            /* Increment per unit value */
  83.   float lw = 0.0;        /* Maximal label width */
  84.   /* Compute maximal label width */
  85.   for (i=0; i<numb; i++)
  86.     if (fl_get_string_width(FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str) > lw)
  87.       lw = fl_get_string_width(FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
  88.   if (lw >0.0) lw += 4.0;
  89.   incr = w/ (max-min);
  90.   zeroh = x-min * incr;
  91.   if ( -min*incr < lw)
  92.     { incr = (w - lw + min*incr)/(max-min); zeroh = x+lw;}
  93.   if (autosize) bwidth = h/numb; else bwidth = h/maxnumb;
  94.   /* Draw base line */
  95.   fl_color(BLACK);
  96.   bgnline(); vv(zeroh,y); vv(zeroh,y+h); endline();
  97.   if (min == 0.0 && max == 0.0) return; /* Nothing else to draw */
  98.   /* Draw the bars */
  99.   for (i=0; i<numb; i++)
  100.     fl_rectbound(zeroh,y+i*bwidth,entries[numb-i-1].val*incr+1.0,bwidth+1.0,
  101.             entries[numb-i-1].col);
  102.   /* Draw the labels */
  103.   fl_color(BLACK);
  104.   for (i=0; i<numb; i++)
  105.     fl_drw_text_beside(FL_ALIGN_LEFT,zeroh+2.0,y+(i+0.5)*bwidth,0.0,0.0,
  106.         BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[numb-i-1].str);
  107. }
  108.  
  109. static void draw_linechart(int type, float x, float y, float w, float h,
  110.             int numb, ENTRY entries[],
  111.             float min, float max, int autosize, int maxnumb)
  112. /* Draws a line chart. x,y,w,h is the bounding box, entries the array of
  113.    numb entries and min and max the boundaries. */
  114. {
  115.   int i;
  116.   float ttt;
  117.   float bwidth;            /* distance between points */
  118.   float zeroh;            /* Height of zero value */
  119.   float incr;            /* Increment per unit value */
  120.   float lh = fl_get_char_height(FL_SMALL_FONT,FL_NORMAL_STYLE);
  121.   incr = (h-2.0*lh)/ (max-min);
  122.   zeroh = y+lh-min * incr;
  123.   if (autosize) bwidth = w/numb; else bwidth = w/maxnumb;
  124.   /* Draw the values */
  125.   for (i=0; i<numb; i++)
  126.   {
  127.     if (type == FL_SPIKE_CHART)
  128.     {
  129.       bgnline();
  130.         fl_color(entries[i].col);
  131.         vv(x+(i+0.5)*bwidth,zeroh);
  132.         vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
  133.       endline();
  134.     }
  135.     else if (type == FL_LINE_CHART && i != 0)
  136.     {
  137.       bgnline();
  138.         fl_color(entries[i-1].col);
  139.         vv(x+(i-0.5)*bwidth,zeroh+entries[i-1].val*incr);
  140.         vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
  141.       endline();
  142.     }
  143.     else if (type == FL_FILLED_CHART && i != 0)
  144.     {
  145.       bgnpolygon();
  146.         fl_color(entries[i-1].col);
  147.         vv(x+(i-0.5)*bwidth,zeroh);
  148.         vv(x+(i-0.5)*bwidth,zeroh+entries[i-1].val*incr);
  149.         if ((entries[i-1].val > 0.0 && entries[i].val < 0.0) ||
  150.             (entries[i-1].val < 0.0 && entries[i].val > 0.0))
  151.         {
  152.           ttt = entries[i-1].val / (entries[i-1].val - entries[i].val);
  153.           vv(x+(i-0.5+ttt)*bwidth,zeroh);
  154.           endpolygon();
  155.           bgnpolygon();
  156.           vv(x+(i-0.5+ttt)*bwidth,zeroh);
  157.         }
  158.         vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
  159.         vv(x+(i+0.5)*bwidth,zeroh);
  160.       endpolygon();
  161.       bgnline();
  162.         fl_color(BLACK);
  163.         vv(x+(i-0.5)*bwidth,zeroh+entries[i-1].val*incr);
  164.         vv(x+(i+0.5)*bwidth,zeroh+entries[i].val*incr);
  165.       endline();
  166.     }
  167.   }
  168.   /* Draw base line */
  169.   fl_color(BLACK);
  170.   bgnline(); vv(x,zeroh); vv(x+w,zeroh); endline();
  171.   /* Draw the labels */
  172.   fl_color(BLACK);
  173.   for (i=0; i<numb; i++)
  174.     if (entries[i].val >= 0.0)
  175.       fl_drw_text_beside(FL_ALIGN_TOP,x+(i+0.5)*bwidth,
  176.         zeroh+entries[i].val*incr,
  177.         0.0,0.0,BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
  178.     else
  179.       fl_drw_text_beside(FL_ALIGN_BOTTOM,x+(i+0.5)*bwidth,
  180.         zeroh+entries[i].val*incr,
  181.         0.0,0.0,BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
  182. }
  183.  
  184. static void draw_piechart(float x, float y, float w, float h,
  185.             int numb, ENTRY entries[], int special)
  186. /* Draws a pie chart. x,y,w,h is the bounding box, entries the array of
  187.    numb entries */
  188. {
  189.   int i;
  190.   float xc,yc,rad;    /* center and radius */
  191.   float tot;        /* sum of values */
  192.   float incr;        /* increment in angle */
  193.   float curang;        /* current angle we are drawing */
  194.   float xl,yl;        /* label position */
  195.   float txc,tyc;    /* temporary center */
  196.   float lh = fl_get_char_height(FL_SMALL_FONT,FL_NORMAL_STYLE);
  197.   /* compute center and radius */
  198.   xc = x+w/2.0; yc = y+h/2.0;
  199.   rad = h/2.0 - lh;
  200.   if (special) { yc -= 0.1*rad; rad = 0.9*rad;}
  201.   /* compute sum of values */
  202.   tot = 0.0;
  203.   for (i=0; i<numb; i++)
  204.     if (entries[i].val > 0.0) tot += entries[i].val;
  205.   if (tot == 0.0) return;
  206.   incr = 3600.0/tot;
  207.   /* Draw the pie */
  208.   curang = 0.0;
  209.   for (i=0; i<numb; i++)
  210.     if (entries[i].val > 0.0)
  211.     {
  212.       txc = xc; tyc = yc;
  213.       /* Correct for special pies */
  214.       if (special && i==0)
  215.       {
  216.         txc += 0.3*rad*cos(ARCINC*(curang+0.5*incr*entries[i].val));
  217.         tyc += 0.3*rad*sin(ARCINC*(curang+0.5*incr*entries[i].val));
  218.       }
  219.       fl_color(entries[i].col);
  220.       arcf(txc,tyc,rad, (int) curang, (int) (curang + incr*entries[i].val));
  221.       fl_color(BLACK);
  222.       arc(txc,tyc,rad, (int) curang, (int) (curang + incr*entries[i].val));
  223.       bgnline();
  224.         vv(txc,tyc); vv(txc+rad*cos(ARCINC*curang),tyc+rad*sin(ARCINC*curang));
  225.       endline();
  226.       curang += 0.5 * incr * entries[i].val;
  227.       /* draw the label */
  228.       xl = txc + 1.1*rad*cos(ARCINC*curang);
  229.       yl = tyc + 1.1*rad*sin(ARCINC*curang);
  230.       if (xl < txc)
  231.         fl_drw_text_beside(FL_ALIGN_LEFT,xl,yl,0.0,0.0,
  232.             BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
  233.       else
  234.         fl_drw_text_beside(FL_ALIGN_RIGHT,xl,yl,0.0,0.0,
  235.             BLACK,FL_SMALL_FONT,FL_NORMAL_STYLE,entries[i].str);
  236.       curang += 0.5 * incr * entries[i].val;
  237.       bgnline();
  238.         vv(txc,tyc); vv(txc+rad*cos(ARCINC*curang),tyc+rad*sin(ARCINC*curang));
  239.       endline();
  240.     }
  241. }
  242.  
  243. static void draw_chart(FL_OBJECT *ob)
  244. /* Draws a chart object */
  245. {
  246.   SPEC *sp = ((SPEC *)(ob->spec));
  247.   float xx,yy,ww,hh;
  248.   float min = sp->min, max = sp->max;
  249.   int i;
  250.   /* Find bounding box */
  251.   xx = ob->x+3.0*FL_CHART_BW;
  252.   yy = ob->y+3.0*FL_CHART_BW;
  253.   ww = ob->w-6.0*FL_CHART_BW;
  254.   hh = ob->h-6.0*FL_CHART_BW;
  255.   /* Find bounds */
  256.   if (min == max)
  257.   {
  258.     min = max = 0.0;
  259.     for (i=0; i<sp->numb; i++)
  260.     {
  261.       if (sp->entries[i].val < min) min = sp->entries[i].val;
  262.       if (sp->entries[i].val > max) max = sp->entries[i].val;
  263.     }
  264.   }
  265.   /* Do the drawing */
  266.   fl_drw_box(ob->boxtype,ob->x,ob->y,ob->w,ob->h,ob->col1,FL_CHART_BW);
  267.   switch (ob->type)
  268.   {
  269.     case FL_BAR_CHART:
  270.     draw_barchart(xx,yy,ww,hh, sp->numb, sp->entries, min, max,
  271.             sp->autosize, sp->maxnumb);
  272.     break;
  273.     case FL_HORBAR_CHART:
  274.     draw_horbarchart(xx,yy,ww,hh, sp->numb, sp->entries, min, max,
  275.             sp->autosize, sp->maxnumb);
  276.     break;
  277.     case FL_PIE_CHART:
  278.     draw_piechart(xx,yy,ww,hh,sp->numb,sp->entries,0);
  279.     break;
  280.     case FL_SPECIALPIE_CHART:
  281.     draw_piechart(xx,yy,ww,hh,sp->numb,sp->entries,1);
  282.     break;
  283.     default:
  284.     draw_linechart(ob->type,xx,yy,ww,hh, sp->numb, sp->entries, min, max,
  285.             sp->autosize, sp->maxnumb);
  286.     break;
  287.   }
  288.   fl_drw_text_beside(ob->align,ob->x,ob->y,ob->w,ob->h,
  289.         ob->lcol,ob->lsize,ob->lstyle,ob->label);
  290. }
  291.  
  292. static int handle_chart(FL_OBJECT *ob,int event,float mx,float my,char key)
  293. /* Handles an event, returns whether value has changed. */
  294. {
  295.   switch (event)
  296.   {
  297.     case FL_DRAW:
  298.     draw_chart(ob);
  299.         return 0;
  300.     case FL_FREEMEM:
  301.     free(ob->spec);
  302.     return 0;
  303.   }
  304.   return 0;
  305. }
  306.  
  307. /*------------------------------*/
  308.  
  309. FL_OBJECT *fl_create_chart(int type,float x,float y,float w,float h,
  310.                 const char *label)
  311. /* creates an object */
  312. {
  313.   FL_OBJECT *ob;
  314.   ob = fl_make_object(FL_CHART,type,x,y,w,h,label,handle_chart);
  315.  
  316.   ob->boxtype = FL_CHART_BOXTYPE;
  317.   ob->col1 = FL_CHART_COL1;
  318.   ob->col2 = FL_CHART_COL1;
  319.   ob->align = FL_CHART_ALIGN;
  320.   ob->lcol = FL_CHART_LCOL;
  321.  
  322.   ob->active = FALSE;
  323.  
  324.   ob->spec = (int *) fl_malloc(sizeof(SPEC));
  325.   ((SPEC *)(ob->spec))->numb = 0;
  326.   ((SPEC *)(ob->spec))->maxnumb = FL_CHART_MAX;
  327.   ((SPEC *)(ob->spec))->autosize = TRUE;
  328.   ((SPEC *)(ob->spec))->min = 0.0;
  329.   ((SPEC *)(ob->spec))->max = 0.0;
  330.  
  331.   return ob;
  332. }
  333.  
  334. FL_OBJECT *fl_add_chart(int type, float x, float y, float w, float h,
  335.                 const char *label)
  336. /* Adds an object */
  337. {
  338.   FL_OBJECT *ob;
  339.   ob = fl_create_chart(type,x,y,w,h,label);
  340.   fl_add_object(fl_current_form,ob);
  341.   return ob;
  342. }
  343.  
  344. void fl_clear_chart(FL_OBJECT *ob)
  345. /* Clears the contents of a chart. */
  346. {
  347.   ((SPEC *)(ob->spec))->numb = 0;
  348.   fl_redraw_object(ob);
  349. }
  350.  
  351. void fl_add_chart_value(FL_OBJECT *ob, float val, char str[], int col)
  352. /* Add an item to the chart. */
  353. {
  354.   SPEC *sp = ((SPEC *)(ob->spec));
  355.   int i;
  356.   /* Shift entries if required */
  357.   if (sp->numb == sp->maxnumb)
  358.   {
  359.     for (i=0; i<sp->numb-1; i++) sp->entries[i] = sp->entries[i+1];
  360.     sp->numb--;
  361.   }
  362.   /* Fill in the new entry */
  363.   sp->entries[sp->numb].val = val;
  364.   sp->entries[sp->numb].col = col;
  365.   strncpy(sp->entries[sp->numb].str,str,16);
  366.   sp->entries[sp->numb].str[15] = '\0';
  367.   sp->numb++;
  368.   fl_redraw_object(ob);
  369. }
  370.  
  371. void fl_insert_chart_value(FL_OBJECT *ob, int index,
  372.             float val, char str[], int col)
  373. /* Inserts an item before index to the chart. */
  374. {
  375.   SPEC *sp = ((SPEC *)(ob->spec));
  376.   int i;
  377.   if (index < 1 || index > sp->numb+1) return;
  378.   /* Shift entries */
  379.   for (i=sp->numb; i >= index; i--) sp->entries[i] = sp->entries[i-1];
  380.   if (sp->numb < sp->maxnumb) sp->numb++;
  381.   /* Fill in the new entry */
  382.   sp->entries[index-1].val = val;
  383.   sp->entries[index-1].col = col;
  384.   strncpy(sp->entries[index-1].str,str,16);
  385.   sp->entries[index-1].str[15] = '\0';
  386.   fl_redraw_object(ob);
  387. }
  388.  
  389. void fl_replace_chart_value(FL_OBJECT *ob, int index,
  390.              float val, char str[], int col)
  391. /* Replaces an item in the chart. */
  392. {
  393.   SPEC *sp = ((SPEC *)(ob->spec));
  394.   if (index < 1 || index > sp->numb) return;
  395.   sp->entries[index-1].val = val;
  396.   sp->entries[index-1].col = col;
  397.   strncpy(sp->entries[index-1].str,str,16);
  398.   sp->entries[index-1].str[15] = '\0';
  399.   fl_redraw_object(ob);
  400. }
  401.  
  402. void fl_set_chart_bounds(FL_OBJECT *ob, float min, float max)
  403. /* Sets the boundaries in the value for the object */
  404. {
  405.   ((SPEC *)(ob->spec))->min = min;
  406.   ((SPEC *)(ob->spec))->max = max;
  407.   fl_redraw_object(ob);
  408. }
  409.  
  410. void fl_set_chart_maxnumb(FL_OBJECT *ob, int maxnumb)
  411. /* Sets the maximal number of values displayed in the chart */
  412. {
  413.   SPEC *sp = ((SPEC *)(ob->spec));
  414.   int i;
  415.   /* Fill in the new number */
  416.   if (maxnumb < 0) return;
  417.   if (maxnumb > FL_CHART_MAX)
  418.     sp->maxnumb = FL_CHART_MAX;
  419.   else
  420.     sp->maxnumb = maxnumb;
  421.   /* Shift entries if required */
  422.   if (sp->numb > sp->maxnumb)
  423.   {
  424.     for (i = 0; i<maxnumb; i++)
  425.       sp->entries[i] = sp->entries[i+sp->numb-maxnumb];
  426.     sp->numb = sp->maxnumb;
  427.     fl_redraw_object(ob);
  428.   }
  429. }
  430.  
  431. void fl_set_chart_autosize(FL_OBJECT *ob, int autosize)
  432. /* Sets whether the chart should autosize along the x-axis */
  433. {
  434.   ((SPEC *)(ob->spec))->autosize = autosize;
  435.   fl_redraw_object(ob);
  436. }
  437.  
  438.